﻿// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Text;

namespace PlatformBenchmarks
{
   // Same as KestrelHttpServer\src\Kestrel.Core\Internal\Http\PipelineExtensions.cs
   // However methods accept T : struct, IBufferWriter<byte> rather than PipeWriter.
   // This allows a struct wrapper to turn CountingBufferWriter into a non-shared generic,
   // while still offering the WriteNumeric extension.

   public static class BufferExtensions
   {
      private const int _maxULongByteLength = 20;

      [ThreadStatic]
      private static byte[] _numericBytesScratch;

      internal static void WriteUtf8String<T>(ref this BufferWriter<T> buffer, string text)
           where T : struct, IBufferWriter<byte>
      {
         var byteCount = Encoding.UTF8.GetByteCount(text);
         buffer.Ensure(byteCount);
         byteCount = Encoding.UTF8.GetBytes(text.AsSpan(), buffer.Span);
         buffer.Advance(byteCount);
      }
      [MethodImpl(MethodImplOptions.NoInlining)]
      internal static void WriteNumericMultiWrite<T>(ref this BufferWriter<T> buffer, uint number)
           where T : IBufferWriter<byte>
      {
         const byte AsciiDigitStart = (byte)'0';

         var value = number;
         var position = _maxULongByteLength;
         var byteBuffer = NumericBytesScratch;
         do
         {
            // Consider using Math.DivRem() if available
            var quotient = value / 10;
            byteBuffer[--position] = (byte)(AsciiDigitStart + (value - quotient * 10)); // 0x30 = '0'
            value = quotient;
         }
         while (value != 0);

         var length = _maxULongByteLength - position;
         buffer.Write(new ReadOnlySpan<byte>(byteBuffer, position, length));
      }

      private static byte[] NumericBytesScratch => _numericBytesScratch ?? CreateNumericBytesScratch();

      [MethodImpl(MethodImplOptions.NoInlining)]
      private static byte[] CreateNumericBytesScratch()
      {
         var bytes = new byte[_maxULongByteLength];
         _numericBytesScratch = bytes;
         return bytes;
      }
   }
}